﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OfficeOpenXml;
using MyBetImporter.DAL;
using MyBetImporter.Scores;

namespace MyBetImporter
{
    public partial class ImportForm : Form
    {
        private delegate void ResultDelegateHandler(string step);
        private ResultDelegateHandler ResultDelegate;
        Dictionary<Guid, int> lastRankDictionary = new Dictionary<Guid, int>();
        Dictionary<Guid, int> differentialDictionary = new Dictionary<Guid, int>();

        private void UpdateProgress(string step)
        {
            // Mise à jour de progression
            ResultList.Items.Add(step);
            ResultList.SelectedIndex = ResultList.Items.Count - 1;
        }

        public ImportForm()
        {
            InitializeComponent();
            FileToImportText.Text = MyBetImporter.Properties.Settings.Default.DefaultImportPath;
            ResultDelegate = new ResultDelegateHandler(UpdateProgress);
            lastRankDictionary = new Dictionary<Guid, int>();
            differentialDictionary = new Dictionary<Guid, int>();
        }

        private void ChooseFileToImportButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                MyBetImporter.Properties.Settings.Default.DefaultImportPath = dlg.FileName;
                MyBetImporter.Properties.Settings.Default.Save();
                FileToImportText.Text = dlg.FileName;
            }
        }

        private void ImportButton_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy != true)
            {
                backgroundWorker1.RunWorkerAsync();
                ImportButton.Enabled = false;
            }
        }

        /// <summary>
        /// Update scores dictionary
        /// </summary>
        /// <param name="currentMatch">Current match to process</param>
        /// <param name="bets">All userbets</param>
        /// <param name="scores">All scores</param>
        private void UpdateScores(ParieurProDBEntities db, Matchs currentMatch, IList<UserBets> bets, ref Dictionary<Guid, Ranks> scores)
        {
            if (currentMatch == null)
            {
                return;
            }

            var query = from bet in bets
                      where bet.MatchId == currentMatch.MatchId
                      select bet;
            IList<UserBets> betList = query.ToList();
            foreach (UserBets bet in betList)
            {
                Ranks rank = new Ranks();
                UserBets currentBet = bet;
                if (!scores.ContainsKey(bet.UserId))
                {
                    scores.Add(bet.UserId, new Ranks());
                }
                else
                {
                    rank = scores[bet.UserId];
                }
                ClassicScore.ComputeScore(currentMatch, ref currentBet, ref rank);
                BetObject.UpdateBetPoint(db, currentBet);
                scores[bet.UserId] = rank;
            }

        }

        private void AddScores(ParieurProDBEntities db, ref Dictionary<Guid, Ranks> total, Dictionary<Guid, Ranks> eventScores, Events evt)
        {
            Dictionary<Guid, Ranks> temporaryDictionary = new Dictionary<Guid, Ranks>();
            long maxScore = 0;

            foreach (Guid userid in eventScores.Keys)
            {
                Ranks rank = eventScores[userid];
                rank.EventCount = 1;

                if (maxScore < rank.TotalScore)
                {
                    maxScore = rank.TotalScore;
                }

                if (total.ContainsKey(userid))
                {
                    total[userid].GoodPredictionCount += rank.GoodPredictionCount;
                    total[userid].GoodScoreCount += rank.GoodScoreCount;
                    total[userid].TotalScore += rank.TotalScore;
                    total[userid].EventCount += 1;
                }
                else
                {
                    total.Add(userid, rank);
                }
            }


            // Order rank by totalscore
            var query = from rnk in total
                        orderby rnk.Value.TotalScore descending, rnk.Value.GoodPredictionCount descending, rnk.Value.GoodScoreCount descending
                        select rnk;
            IList<KeyValuePair<Guid, Ranks>> rankList = query.ToList();

            int position=1;
            foreach (KeyValuePair<Guid, Ranks> pair in rankList)
            {
                Ranks tmpRank = pair.Value;
                tmpRank.CurrentRank=position;
                temporaryDictionary.Add(pair.Key, tmpRank);
                position++;
            }

            if (eventScores.Count>0)
            {
                foreach (Guid userid in total.Keys)
                {
                    Ranks rank;
                    byte isBestScore = 0;

                    if (eventScores.ContainsKey(userid))
                    {
                        rank = eventScores[userid];
                        if (rank.TotalScore==maxScore)
                        {
                            isBestScore=1;
                        }
                    }
                    else
                    {
                        rank = new Ranks();
                    }

                    rank.CurrentRank = temporaryDictionary[userid].CurrentRank;
                    // Store event score in database
                    EventObject.AddOrUpdateScore(db, userid, evt, rank, isBestScore);

                    if (lastRankDictionary.ContainsKey(userid))
                    {
                        if (differentialDictionary.ContainsKey(userid))
                        {
                            differentialDictionary[userid] = lastRankDictionary[userid] - rank.CurrentRank;
                        }
                        else
                        {
                            differentialDictionary.Add(userid, lastRankDictionary[userid] - rank.CurrentRank);
                        }
                        lastRankDictionary[userid] = rank.CurrentRank;
                    }
                    else
                    {
                        lastRankDictionary.Add(userid, rank.CurrentRank);
                        differentialDictionary.Add(userid, 0);
                    }
                }
            }
        }

        /// <summary>
        /// Create or update rank
        /// </summary>
        /// <param name="db"></param>
        /// <param name="total"></param>
        private void UpdateRank(ParieurProDBEntities db, Dictionary<Guid, Ranks> total)
        {
            List<Ranks> ranks = new List<Ranks>();
            //int position = 1;

            foreach (Guid userId in total.Keys)
            { 
                // Get subscription id
                int id = SubscriptionObject.GetSubscriptionId(db, userId);
                Ranks rank = total[userId];
                rank.CurrentRank = lastRankDictionary[userId];
                rank.RankDifferential = null;
                if (differentialDictionary.ContainsKey(userId))
                {
                    rank.RankDifferential = differentialDictionary[userId];
                }
                rank.SubscriptionId = id;
                RankObject.AddOrUpdate(db, rank);
            }

            //// Order rank by totalscore
            //var query = from rnk in ranks
            //            orderby rnk.TotalScore descending, rnk.GoodScoreCount descending, rnk.GoodPredictionCount descending
            //            select rnk;
            //IList<Ranks> rankList = query.ToList();

            //foreach (Ranks currentRank in rankList)
            //{
            //    // Create new rank
            //    currentRank.CurrentRank = position;
            //    currentRank.RankDifferential = null;
                
            //    RankObject.AddOrUpdate(db, currentRank);
            //    position++;
            //}

        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            System.IO.FileInfo fileIn = new System.IO.FileInfo(FileToImportText.Text);
            Dictionary<string, int> teamsId = new Dictionary<string,int>();

            using (ExcelPackage pkg = new ExcelPackage(fileIn))
            {
                // get the first worksheet in the workbook
                ExcelWorksheet worksheet = pkg.Workbook.Worksheets[1];
                // Read first line to get ChampionshipId and seasonId
                int championshipId = Convert.ToInt32(worksheet.Cells[1, 2].Value);
                int seasonId = Convert.ToInt32(worksheet.Cells[1, 4].Value);
                // Current day
                int currentEvent = 0;
                // Current line
                int line = 2;
                // File is finished ?
                bool bContinue = true;
                DateTime dayDate = DateTime.UtcNow;
                DateTime closeDate = DateTime.UtcNow;
                DateTime openDate = DateTime.UtcNow;
                Events evt=null;
                Dictionary<Guid, Ranks> scores = new Dictionary<Guid,Ranks>();

                using (ParieurProDBEntities db = new ParieurProDBEntities())
                {
                    Dictionary<Guid, Ranks> eventScores = new Dictionary<Guid, Ranks>();
                    IList<UserBets> bets = new List<UserBets>();

                    int? contestId = ContestObject.GetId(db, championshipId, seasonId);
                    if (!contestId.HasValue)
                    {
                        MessageBox.Show("No contest found");
                        return;
                    }

                    IList<Guid> users = SubscriptionObject.GetUsersList(db, contestId.Value);
                    foreach (Guid user in users)
                    {
                        scores.Add(user, new Ranks());
                    }

                    // Read match information
                    do
                    {
                        bContinue=(worksheet.Cells[line, 1].Value!=null);
                        if (bContinue)
                        {
                            int dayId = Convert.ToInt32(worksheet.Cells[line, 1].Value);
                            if (currentEvent != dayId)
                            {
                                // Store event score somewhere
                                this.Invoke(this.ResultDelegate, new object[] { String.Format("Process {0} day", dayId) });
                                
                                if (evt != null)
                                {
                                    if (evt.CloseDate < DateTime.UtcNow)
                                    {
                                        AddScores(db, ref scores, eventScores, evt);
                                    }
                                }
                                eventScores.Clear();
                                dayDate = DateTime.FromOADate(Convert.ToDouble(worksheet.Cells[line, 2].Value));
                                dayDate = new DateTime(dayDate.Year, dayDate.Month, dayDate.Day, 19, 0, 0);
                                TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
                                dayDate = TimeZoneInfo.ConvertTime(dayDate, tzi);
                                closeDate = new DateTime(dayDate.ToUniversalTime().Year, dayDate.ToUniversalTime().Month, dayDate.ToUniversalTime().Day, dayDate.ToUniversalTime().Hour, dayDate.ToUniversalTime().Minute, dayDate.ToUniversalTime().Second);
                                openDate = closeDate.AddDays(-2);
                                // Verify if day exists in database
                                evt = EventObject.GetEventFromDatabase(db, championshipId, seasonId, dayId);
                                EventObject.AddOrUpdate(db, evt, contestId.Value, dayId, openDate, closeDate);
                                evt = EventObject.GetEventFromDatabase(db, championshipId, seasonId, dayId);
                                currentEvent = dayId;
                                // Get all bets for this event
                                bets = BetObject.GetAllBetsInDatabase(db, championshipId, dayId, seasonId);
                            }
                            
                            string dayHomeTeam = Convert.ToString(worksheet.Cells[line, 3].Value);
                            byte? dayScoreHome = null;
                            byte? dayScoreAway = null;
                            string dayAwayTeam = Convert.ToString(worksheet.Cells[line, 6].Value);
                            int tid = -1;
                            Matchs currentMatch;

                            if (worksheet.Cells[line, 4].Value != null)
                            {
                                dayScoreHome = Convert.ToByte(worksheet.Cells[line, 4].Value);
                            }
                            if (worksheet.Cells[line, 5].Value != null)
                            {
                                dayScoreAway = Convert.ToByte(worksheet.Cells[line, 5].Value);
                            }

                            if (!teamsId.ContainsKey(dayHomeTeam.ToUpper()))
                            {
                                tid = TeamObject.GetTeamId(db, dayHomeTeam.ToUpper());
                                if (tid == -1)
                                {
                                    throw new Exception("Unknown team " + dayHomeTeam);
                                }
                                teamsId.Add(dayHomeTeam.ToUpper(), tid);
                            }
                            if (!teamsId.ContainsKey(dayAwayTeam.ToUpper()))
                            {
                                tid = TeamObject.GetTeamId(db, dayAwayTeam.ToUpper());
                                if (tid == -1)
                                {
                                    throw new Exception("Unknown team " + dayAwayTeam);
                                }
                                teamsId.Add(dayAwayTeam.ToUpper(), tid);
                            }
                            // update match in database

                            currentMatch = MatchObject.AddOrUpdate(db, closeDate, teamsId[dayHomeTeam.ToUpper()], teamsId[dayAwayTeam.ToUpper()], dayScoreHome, dayScoreAway, evt.EventId);

                            if (evt.CloseDate < DateTime.UtcNow)
                            {
                                UpdateScores(db, currentMatch, bets, ref eventScores);
                            }
                            else
                            {
                                System.Diagnostics.Trace.WriteLine("not started...");
                            }

                            line = line + 1;
                        }

                    } while (bContinue);

                    if (evt.CloseDate < DateTime.UtcNow)
                    {
                        AddScores(db, ref scores, eventScores, evt);
                    }
                    // Store current rank somewhere
                    UpdateRank(db, scores);
                    db.SaveChanges();
                }

            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            ImportButton.Enabled = true;
        }
    }
}
